﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TharpT9Lab
{
    
    public partial class MainWindow : Window
    {
        private T9Word _t9 = new T9Word();
        /// <summary>
        /// Sets up the main window, adds
        /// the buttons and textbox
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();


            //declare the textbox
            T9ViewTextBox theTextBox = new T9ViewTextBox(_t9);
            //declare the buttons grid
            T9ButtonArray theButtons = new T9ButtonArray( _t9);

            //add the buttons grid as the content of the main window
            MainGrid.Children.Add(theButtons);

            ///add the textbox to the content of the main window
            MainGrid.Children.Add(theTextBox);

            ///make sure the width and height of the main window are sufficient
            this.Width = Math.Max(theButtons.Width, theTextBox.Width);
            this.Height =
                theButtons.Height +
                theTextBox.Height +
                theTextBox.Margin.Top +
                theTextBox.Margin.Bottom;

        }//end MainWindow()


    }//end Class MainWindow

    /// <summary>
    /// This class represents a grid of T9Buttons
    /// </summary>
    public class T9ButtonArray : System.Windows.Controls.Grid,
                                IEnumerable<T9Button>
    {
        /// <summary>
        /// A property that can be set in XAML
        /// representing the number of rows of buttons
        /// </summary>
        public static readonly DependencyProperty RowsProperty =
            DependencyProperty.Register(
            "Rows",
            typeof(int),
            typeof(T9ButtonArray));
        /// <summary>
        /// A property that can be set in XAML
        /// representing the number of columns of buttons
        /// </summary>
        public static readonly DependencyProperty ColumnsProperty =
            DependencyProperty.Register(
            "Columns",
            typeof(int),
            typeof(T9ButtonArray));
        /// <summary>
        /// A property that can be set in XAML
        /// representing the contents of the buttons
        /// </summary>
        public static readonly DependencyProperty ButtonsProperty =
            DependencyProperty.Register(
            "Buttons",
            typeof(String),
            typeof(T9ButtonArray));


        /// <summary>
        /// A property that can be set in XAML
        /// representing the number of rows of buttons
        /// </summary>
        public int Rows
        {
            get { return (int)GetValue(RowsProperty); }
            set { SetValue(RowsProperty, value); }
        }//end Rows

        /// <summary>
        /// A property that can be set in XAML
        /// representing the number of columns of buttons
        /// </summary>
        public int Columns
        {
            get { return (int)GetValue(ColumnsProperty); }
            set { SetValue(ColumnsProperty, value); }
        }//end Rows

        /// <summary>
        /// A property that can be set in XAML
        /// representing the contents of the buttons
        /// </summary>
        public String Buttons
        {
            get { return (string)GetValue(ButtonsProperty); }
            set { SetValue(ButtonsProperty, value); }
        }//end Rows

        private T9Button[,] _buttonArray;

        /// <summary>
        /// A constructor that initializes the button grid based on
        /// the Rows, Columns, and Buttons properties
        /// which have been set in the XAML
        /// </summary>
        /// <param name="word">The T9Word that the buttons control</param>
        public T9ButtonArray(T9Word word) : base()
        {
            ///retrieve the dependency properties from the XAML
            this.Style = (Style)Application.Current.Resources["ButtonArrayStyle"];

            //make sure the Buttons string makes sense
            String[] buttonsString = Buttons.Split(' ');
            System.Diagnostics.Debug.Assert(Rows > 0);
            System.Diagnostics.Debug.Assert(Columns > 0);
            System.Diagnostics.Debug.Assert(
                buttonsString.Length <= Rows * Columns);

            _buttonArray = new T9Button[Rows,Columns];
            
            int currentButton = 0;
            for (int row = 0; row < Rows; row++)
            {
                for (int column = 0; column < Columns; column++)
                {
                    //parse the content of the Buttons string and turn it into T9Buttons
                    _buttonArray[row,column] =
                        ParseButtonString(buttonsString[currentButton]);
                    _buttonArray[row,column].TheT9Word = word;
                    _buttonArray[row, column].Style =
                        (Style)Application.Current.Resources["ButtonStyle"];
                    _buttonArray[row,column].SetPosition(column,row);
                    this.Children.Add(_buttonArray[row, column]);
                    if (++currentButton >= buttonsString.Length)
                        break;
                }//end for column
                if (currentButton >= buttonsString.Length)
                    break;
            }//end for break
            var aButton = _buttonArray[0, 0];
            this.Width =
                Columns * (aButton.Width + 2 * aButton.Margin.Right);
            this.Height =
                Rows * (aButton.Height + 2 * aButton.Margin.Bottom);
        }//end T9ButtonArray()

        /// <summary>
        /// A parser that turns a Button string into a T9Button
        /// </summary>
        /// <param name="btnString">The string representing the content of the button</param>
        /// <returns>A T9Button object</returns>
        private T9Button ParseButtonString(String btnString)
        {
            switch(btnString.Substring(0,1))
            {
                case "D":
                    string digit = btnString.Substring(1,1);
                    return new T9Button(digit, new T9Word.DigitCommand(digit));
                case "B":
                    return new T9Button(
                        T9InputCommand.BACK,
                        new T9Word.BackCommand());
                case "N":
                    return new T9Button(
                        T9InputCommand.NEXT,
                        new T9Word.NextCommand());
                case "S":
                    return new T9Button(
                        T9InputCommand.SPACE,
                        new T9Word.SpaceCommand());
                default:
                    throw new ArgumentException(
                        "The Button String was invalid: " + btnString);
            }//end switch
        }//end T9Button ParseButtonString(String)

        /// <summary>
        /// Enumerates through the button array
        /// </summary>
        /// <returns>An IEnumerator of T9Buttons</returns>
        public IEnumerator<T9Button> GetEnumerator()
        {
            foreach (var btn in _buttonArray)
                yield return btn;
        }//end GetEnumerator

        /// <summary>
        /// Enumerates through the button array
        /// </summary>
        /// <returns>An IEnumerator of T9Buttons</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }//end System.Collections.IEnumerable.GetEnumerator()

    }//end class T9ButtonArray

    /// <summary>
    /// A class representing a button that controls a T9Word object
    /// </summary>
    public class T9Button : Button
    {
        private string _Label = String.Empty;
        private int _X, _Y;

        /// <summary>
        /// The command associated with the button
        /// </summary>
        public T9InputCommand T9Command { get; private set; }

        /// <summary>
        /// The T9Word the button controls
        /// </summary>
        public T9Word TheT9Word;

        /// <summary>
        /// A positional property if the button is to be placed in a grid
        /// </summary>
        public int XPos { get { return _X; } private set { _X = value; } }

        /// <summary>
        /// A positional property if the button is to be placed in a gird
        /// </summary>
        public int YPos { get { return _Y; } private set { _Y = value; } }

        /// <summary>
        /// The label on the button
        /// </summary>
        public string Label
        {
            get { return _Label; }
            private set { _Label = value; }
        }///end Label

        /// <summary>
        /// A map of digits to labels
        /// </summary>
        private static readonly Dictionary<String,String> _Labels =
            new Dictionary<string,string>
        {
            {T9InputCommand.QUIT, ""},
            {T9InputCommand.BACK, "<"},
            {T9InputCommand.NEXT, "~"},
            {T9InputCommand.SPACE, ""},
            {"2", "abc"},
            {"3", "def"},
            {"4", "ghi"},
            {"5", "jkl"},
            {"6", "mno"},
            {"7", "pqrs"},
            {"8", "tuv"},
            {"9", "wxyz"}
        };

        /// <summary>
        /// A constructor that sets up all the properties of the button
        /// </summary>
        /// <param name="label">What the label of the command is</param>
        /// <param name="command">What command the button should perform</param>
        /// <param name="word">The T9Word the button commands</param>
        /// <param name="x">The x position of the button in a gird</param>
        /// <param name="y">The y position of the button in a gird</param>
        public T9Button(string label, T9InputCommand command, T9Word word, int x, int y) : base()
        {
            this.Label = label;
            this.T9Command = command;
            this.TheT9Word = word;
            this.XPos = x;
            this.YPos = y;

            this.Content = this.Label + System.Environment.NewLine + this.T9Command.CommandName;

        }//end T9Button

        /// <summary>
        /// A constructor that sets up the label and command of the button
        /// Should be used in conjuction with SetPosition(int,int)
        /// and the TheT9Word property to fully configure the button.
        /// </summary>
        /// <param name="label">The label of the command the button performs</param>
        /// <param name="command">The command the button performs</param>
        public T9Button(string label, T9InputCommand command)
            : base()
        {
            this.Label = label;
            this.Content = ParseLabel(this.Label);
            this.T9Command = command;
        }//end T9Button(string, T9InputCommand)

        /// <summary>
        /// Parses a command label into the full Content
        /// that should be shown on the button
        /// </summary>
        /// <param name="label">The command label</param>
        /// <returns>A string that should be the Content of the button</returns>
        private String ParseLabel(String label)
        {
            System.Diagnostics.Debug.Assert(_Labels.ContainsKey(label));
            return label + System.Environment.NewLine + _Labels[label];

        }//end String ParseLabel(String)

        /// <summary>
        /// Sets the position of the button in it's parent
        /// based on the XPos and YPos
        /// </summary>
        public void SetPosition()
        {
            double topMargin = (YPos * Height) + ((YPos + 1) * Margin.Top);
            double leftMargin = (XPos * Width) + ((XPos + 1) * Margin.Left);

            this.Margin = new Thickness(leftMargin, topMargin, this.Margin.Right, this.Margin.Bottom);
        }//end SetPosition()

        /// <summary>
        /// Sets the XPos and YPos of the button,
        /// then sets it's position in it's parent
        /// </summary>
        /// <param name="x">The XPos of the button in a grid</param>
        /// <param name="y">The YPos of the button in a grid</param>
        public void SetPosition(int x, int y)
        {
            this.XPos = x;
            this.YPos = y;

            double topMargin = (YPos * Height) + ((YPos + 1) * Margin.Top);
            double leftMargin = (XPos * Width) + ((XPos + 1) * Margin.Left);

            this.Margin = new Thickness(
                leftMargin,
                topMargin,
                this.Margin.Right,
                this.Margin.Bottom);
        }//end SetPosition()

    }//end class T9Button

    /// <summary>
    /// A class representing a TextBox view of a T9Word
    /// </summary>
    public class T9ViewTextBox : TextBox, T9Observer
    {
        private T9Word _theWord;
        private Queue<String> _theHistory = new Queue<string>();

        private static int MAX_LENGTH = 255;

        /// <summary>
        /// Constructor which sets the T9Word to view
        /// </summary>
        /// <param name="t9Word"></param>
        public T9ViewTextBox(T9Word t9Word)
            : base()
        {
            _theWord = t9Word;
            ///set up the textbox from the XAML style
            this.Style = (Style)(Application.Current.Resources["TextBoxStyle"]);
            ///register the textbox as view of the T9Word
            _theWord.Register(this);
        }

        /// <summary>
        /// Sets the position of the text box in it's parent based on the
        /// position of the last control above the text box
        /// </summary>
        /// <param name="ctl">The control immediately above the text box</param>
        public void SetPosition(Control ctl)
        {
            double topMargin = ctl.Margin.Top + ctl.Height + this.Margin.Top;

            this.Margin = new Thickness(
                this.Margin.Left,
                topMargin,
                this.Margin.Right,
                this.Margin.Bottom);
        }//end SetPosition(Control)

        /// <summary>
        /// Updates the view of the T9Word.
        /// Should be called by the T9Word when it's data has changed.
        /// </summary>
        public void Update()
        {
            //fectch the new t9word
            string currentDisplayWord = _theWord.DisplayString;

            //see if the word is completed
            if (_theWord.IsComplete)
            {
                _theHistory.Enqueue(currentDisplayWord);
                this.RefreshText();
            }
            else
            {
                this.RefreshText(currentDisplayWord);
            }//end if else
        }//end Update()

        /// <summary>
        /// Refreshes the Text of the text box.
        /// Should be called whenever currentDisplayWord changes
        /// </summary>
        /// <param name="lastWord">The last word to be displayed in the text box</param>
        private void RefreshText(string lastWord = "")
        {
            this.Text = String.Empty;
            foreach (var word in _theHistory)
                this.Text += word + " ";
            this.Text += lastWord;

            //make sure to only display MAX_LENGTH characters
            this.Text = this.Text.Substring(
                Math.Max(this.Text.Length - MAX_LENGTH, 0));
        }//end RefreshText

    }//endT9ViewTextBox




}//end namespace TharpT9Lab
